package controllers;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import models.EEntity;
import models.Organization;
import models.RelatedEntities;
import models.User;
import play.data.validation.Required;
import play.data.validation.Validation;
import play.data.validation.Validation.ValidationResult;
import play.mvc.Controller;

/**
 * 
 * @author Yomna El Rashidi
 * @version 1.0
 * 
 * 
 * 
 */
public class Relations extends Controller {

	/**
	 * views all the relations between a certain entity with any other entities
	 * 
	 * @author Yomna El Rashidi
	 * @param eentityID
	 *            The ID of the entity to show its relations
	 * 
	 * 
	 */

	public static void displayRelations(long eentityID) {
		EEntity temp = EEntity.findById(eentityID);
		List<RelatedEntities> relations = temp.relations;
		render(relations);
	}

	/**
	 * @author Yomna El Rashidi
	 * renders the view for creation of relations between entities
	 * 
	 * 
	 * @param orgID
	 *            the ID of the organization the user wants to create the
	 *            relation in
	 * @param entityID
	 *            the entity I am currently in and I want to create a relation
	 *            with
	 */

	public static void AllowedRelationCreation(long orgID, long entityID) {
		String userName = session.get("username");
		User user = null;
		if (userName != null) {
			user = User.find("byUsername", userName).first();
		}

		if (user == null) {
			return;
		}

		Organization org = Organization.find("byID", orgID).first();
		EEntity ent = EEntity.find("byID", entityID).first();
		List<EEntity> allEntities = org.getEntities();
		List<EEntity> remainingEntities = new ArrayList<EEntity>();
		for (EEntity entity1 : allEntities) {
			if (!entity1.equals(ent)) {
				remainingEntities.add(entity1);
			}
		}
		boolean permitted = user.isOrganizationLead(orgID);

		render(permitted, remainingEntities, entityID);

	}

	/**
	 *  @author Yomna El Rashidi
	 * renders the view of renaming a relation between entities
	 *  
	 * @param orgID
	 *            the ID of the organization the user wants to rename the
	 *            relation in
	 * @param entityID
	 *            the ID of the entity that forms the first part of the relation
	 *            to be renamed
	 */

	public static void AllowedRelationRenaming(long orgID, long entityID) {
		String userName = session.get("username");
		User user = null;
		if (userName != null) {
			user = User.find("byUsername", userName).first();
		}
		if (user == null)
			return;
		EEntity wanted = EEntity.find("byID", entityID).first();
		List<RelatedEntities> relations = wanted.relations;
		boolean permitted = user.isOrganizationLead(orgID);
		render(permitted, relations, entityID);
	}

	/**
	 * 
	 * @author Yomna El Rashidi
	 * renders the view of deletion of a relation between entities
	 * 
	 * @param orgID
	 *            the ID of the organization the user wants to delete the
	 *            relation from
	 * @param entityID
	 *            the ID of the entity that we want to delete ne of its
	 *            relations
	 */

	public static void AllowedRelationDeletion(long orgID, long entityID) {
		String userName = session.get("username");
		User user = null;
		if (userName != null) {
			user = User.find("byUsername", userName).first();
		}
		if (user == null)
			return;
		EEntity wanted = EEntity.find("byID", entityID).first();
		List<RelatedEntities> relations = wanted.relations;
		boolean permitted = user.isOrganizationLead(orgID);
		render(permitted, relations);
	}

	/**
	 *  @author Yomna El Rashidi
	 * creates a relation between the entities
	 * 
	 * @param entity
	 *            this entity's ID taken from the query string
	 * @param entity2
	 *            the entity that the relation is going to be created with
	 * @param name
	 *            the name of the relation
	 * @param description
	 *            the description of the relation
	 */

	public static void createRelation(long entity, long entity2,String name,
			 String description) {
		validation.required(name).message("the relation name is required");
		validation.required(description).message("the relation description is required");
		validation.minSize(name,3).message("the name of the relation should atleast be 3 letters long");
		validation.minSize(description, 10).message("the description should be atleast 10 characters long");
		ValidationResult validResult = validation.match(name,"^[a-z -']+$").message("the name of the relation should be letters only "); 
		if (validation.hasErrors() || validResult.ok !=true) {
	
			params.flash();
			validation.keep();
			Browse.viewEntity(entity);
		}		
		else{
		EEntity toRelate = EEntity.find("byID", entity).first();
		EEntity toRelate2 = EEntity.find("byID", entity2).first();
		RelatedEntities.createRelation(toRelate, toRelate2, name, description);
		flash.success("you have successfully created the relation !");
		Browse.viewEntity(entity);
		}
	}

	/**
	 * 
	 * @author Yomna El Rashidi
	 * 
	 * renames relations between entities within an organization
	 *
	 * @param entity
	 *            the ID of the first entity in the relation to rename
	 * @param entity2 
	 * 			  the ID of the second entity in the relation to rename 
	 * @param name
	 *            the new name of the relation
	 */
	public static void renameRelation(long entity,long entity2, String name) {
		validation.required(name).message("the new name of the relation is required !");
		validation.minSize(name, 3).message("the new name should be atleast 3 letters long");
		ValidationResult validResult = validation.match(name,"^[a-z -']+$").message("only letters are allowed in the new name ");
		if (validation.hasErrors() || validResult.ok !=true) {
			params.flash();
			validation.keep();
			
			Browse.viewEntity(entity);
		}	
		else{
		EEntity firstEntity = EEntity.find("byID", entity).first();
		EEntity secondEntity = EEntity.find("byID", entity2).first();
		RelatedEntities relation= Relations.getRelation(firstEntity,secondEntity);
		long relationID= relation.id;
		RelatedEntities.renameRelation(relationID, name);
		flash.success("you have successfully renamed the relation !");
		Browse.viewEntity(entity);
		}
	}

	/**
	 * deletes relations between entities within an organization
	 * 
	 * @author Yomna El Rashidi
	 * 
	 * @param entity 
	 * 				the ID of the first entity in the relation to delete
	 * @param entity2 
	 * 				the ID of the second entity in the relation to delete 
	 */

	public static void deleteRelation(long entity,long entity2) {
		EEntity firstEntity = EEntity.find("byID", entity).first();
		EEntity secondEntity = EEntity.find("byID", entity2).first();
		RelatedEntities relation= Relations.getRelation(firstEntity,secondEntity);
		long relationID= relation.id;
		RelatedEntities.deleteRelation(relationID);
		flash.success("you have successfully deleted the relation ! ");
		Browse.viewEntity(entity);
	}

	/**
	 * @author Yomna El Rashidi
	 * 
	 *         returns a list of entities related to a certain entity
	 * 
	 * @param entityID
	 *            the ID of the entity to find related entities with
	 *            
	 * @return list of entities which is related to the one of the given ID ,
	 *         the list is null if there are no related entities
	 */
	public static List<EEntity> getRelatedWithEntities(long entityID) {
		EEntity entity = EEntity.find("byID", entityID).first();
		List<RelatedEntities> relations = entity.relations;
		List<RelatedEntities> relatedTo = entity.relatesTo;
		List<EEntity> relatedEntities = new LinkedList<EEntity>();
		if (relations != null) {
			for (RelatedEntities relatedOnes : relations){ 
					relatedEntities.add(relatedOnes.secondEntity);
				}
		}
		if(relatedTo !=null){
			for (RelatedEntities relatedOnes : relatedTo){ 
				relatedEntities.add(relatedOnes.firstEntity);
			}
		}
		return relatedEntities;
	}

	/***
	 * @author Yomna El Rashidi
	 * 
	 *  finds the entities within an organization which are not yet
	 *  related to a certain entity and are not locked , so they can be
	 *  related and returns a list of these entities
	 * 
	 * @param entityID
	 *            the ID of the entity to find available entities to relate to
	 * @param org
	 *            the ID of the organization which holds this entity
	 * @return returns the list of available entities for relation, if there are
	 *         no available entities , it'll return null
	 */
	public static List<EEntity> getallowedToRelateEntities(long entityID,
			Organization org) {

		EEntity entity = EEntity.find("byID", entityID).first();
		List<EEntity> allEntities = org.entities;
		List<EEntity> otherEntities = new LinkedList<EEntity>();
		List<EEntity> relatedEntities = getRelatedWithEntities(entityID);
		boolean flag = true;
		for (EEntity toCheckEntity : allEntities) {
			if (!toCheckEntity.equals(entity)) {
				if (!toCheckEntity.isLock) {
					if (relatedEntities.size()!=0) {
						for (EEntity alreadyRelatedOne : relatedEntities) {
							if (alreadyRelatedOne.equals(toCheckEntity)
									&& flag == true) {
								flag = false;
								
							}
						}
					}
				} else
					flag = false;

				if (flag) {
					otherEntities.add(toCheckEntity);
					
				}
				flag = true;
			}
		}
		return otherEntities;
	}

	/**
	 * @author Yomna El Rashidi
	 * 
	 * gets the name of the relation between two entities
	 * 
	 * @param entity1
	 *            the first entity within the relation
	 * @param entity2
	 *            the second entity within the relation
	 * @return returns the relation (relatedEntities) between the
	 *         entities.
	 */


	public static RelatedEntities getRelation(EEntity entity1, EEntity entity2) {
		List<RelatedEntities> relations = entity1.relations;
		List<RelatedEntities> relatedTo = entity1.relatesTo;
		boolean found = false;
		RelatedEntities relationNeeded= new RelatedEntities(null,null,"","");
		for (RelatedEntities relation : relations) {
			if (relation.firstEntity.equals(entity1)
					&& relation.secondEntity.equals(entity2)) {
				found = true;
				relationNeeded = relation;
				break;
			}
			
		}
		if(!found){
			for(RelatedEntities relatedToIt : relatedTo){

				if (relatedToIt.firstEntity.equals(entity2)
						&& relatedToIt.secondEntity.equals(entity1)) {
			    found = true;
			    relationNeeded=relatedToIt;
			    break;
				}
		}
	}
			return relationNeeded;
	
	}

	/**
	 * @author Yomna El Rashidi 
	 * 
	 * locks the entity to prevent any other entity
	 * from being related to it
	 * 
	 * @param entityID
	 *            the entity to be locked
	 * 
	 */
	public static void lockEntity(long entityID) {
		EEntity entity = EEntity.find("byID", entityID).first();
		entity.isLock = true;
		entity.save();
		Browse.viewEntity(entityID);
	}

	/**
	 * @author Yomna El Rashidi 
	 * finds whether an entity is locked or not , it
	 *         can't be related to other entities or not
	 *         
	 * @param entityID
	 *            the ID of the entity to check its lock
	 *            
	 * @return return boolean value, true if the entity is locked and false if
	 *         the entity is no locked
	 */
	public static boolean isLocked(long entityID) {
		EEntity entity = EEntity.find("byID", entityID).first();
		return entity.isLock;
	}

	/**
	 * @author Yomna El Rashidi
	 * 
	 * sets the lock of an entity to false
	 * 
	 * @param entityID
	 *            the ID of the entity to unlock
	 */
	public static void unlockEntity(long entityID) {
		EEntity entity = EEntity.find("byID", entityID).first();
		entity.isLock = false;
		entity.save();
		Browse.viewEntity(entityID);
	}

}
